package engine;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

public class SparseVectorTI extends AbstractVectorTI<SparseVectorTI> {

    TreeMap<Integer, Double> values;
    Integer vocSize;

    public SparseVectorTI(Integer size, Integer vocabularySize) {
        super(size);

        values = new TreeMap<Integer, Double>();
        this.vocSize = vocabularySize;
    }

    @Override
    public Integer size() {
        return values.size();
    }

    @Override
    public AbstractVectorTI clone() {
        SparseVectorTI copy = new SparseVectorTI(values.size(), this.vocSize);

        for (Map.Entry<Integer, Double> entry : values.entrySet()) {
            copy.put(entry.getKey(), entry.getValue());
        }

        return copy;
    }

    @Override
    public void put(Integer wordID, Double value) {
        values.put(wordID, value);
    }

    @Override
    public Double get(Integer wordID) {
        return values.get(wordID);
    }

    public Double safeGet(Integer wordID) {
        if (values.containsKey(wordID)) {//coute O(log(N))
            return values.get(wordID);
        } else {
            return 0d;
        }
    }

    @Override
    public Double norm() {
        double sum = 0d;

        for (Map.Entry<Integer, Double> entry : values.entrySet()) {
            sum += entry.getValue() * entry.getValue();
        }

        return Math.sqrt(sum);
    }

    @Override
    public AbstractVectorTI normalize() {
        SparseVectorTI normalizedVector = new SparseVectorTI(values.size(), this.vocSize);

        Double norm = norm();

        //Si la norme est nulle, on retourne le vecteur non modifié
        if (norm == 0d) {
            return clone();
        }

        for (Map.Entry<Integer, Double> entry : values.entrySet()) {
            normalizedVector.put(entry.getKey(), entry.getValue() / norm);
        }

        return normalizedVector;
    }

    @Override
    public Double distanceManhattan(SparseVectorTI v1, SparseVectorTI v2) {
        Double sum = 0d;

        for (int i = 0; i < this.vocSize; i++) {
            sum += Math.abs(v1.safeGet(i) - v2.safeGet(i));
        }

        return sum;
    }

    @Override
    public Double distanceEuclidienne(SparseVectorTI v1, SparseVectorTI v2) {
        Double sum = 0d;
        Double currentDifference;

        for (int i = 0; i < this.vocSize; i++) {
            currentDifference = Math.abs(v1.safeGet(i) - v2.safeGet(i));
            sum += currentDifference * currentDifference;
        }

        return Math.sqrt(sum);
    }

    @Override
    public Double distanceMinkowski(SparseVectorTI v1, SparseVectorTI v2, Integer parameter) {
        Double sum = 0d;
        Double currentDifference;
        for (int i = 0; i < this.vocSize; i++) {
            currentDifference = Math.abs(v1.safeGet(i) - v2.safeGet(i));
            sum += Math.pow(currentDifference, parameter);
        }

        return Math.pow(sum, 1 / parameter);
    }

    @Override
    public AbstractVectorTI average(ArrayList<SparseVectorTI> vectors) {
        Integer vectorsCount = vectors.size();
        if (vectorsCount == 0) {
            return null;
        }

        SparseVectorTI averageVector = new SparseVectorTI(vectors.get(0).size(), this.vocSize);
        Double currentIdAvg;

        for (int i = 0; i < this.vocSize; i++) {

            currentIdAvg = 0d;
            for (SparseVectorTI v : vectors) {
                currentIdAvg += v.safeGet(i);
            }

            averageVector.put(i, currentIdAvg / vectorsCount);
        }

        return averageVector;
    }

    @Override
    public String toString() {
        DecimalFormat twoDecimals = new DecimalFormat("#.##");

        String print = "";

        for (Map.Entry<Integer, Double> entry : values.entrySet()) {
            print += twoDecimals.format(entry.getValue()) + "; ";
        }

        return print;
    }

    @Override
    public String toString(ArrayList<String> voc) {
        DecimalFormat twoDecimals = new DecimalFormat("#.##");

        String print = "";

        for (Map.Entry<Integer, Double> entry : values.entrySet()) {
            print += voc.get(entry.getKey()) + " : " + twoDecimals.format(entry.getValue()) + "; ";
        }

        return print;
    }

    @Override
    public String toShortString(ArrayList<String> voc, double weightMin) {
        DecimalFormat twoDecimals = new DecimalFormat("#.##");

        String print = "";

        for (Map.Entry<Integer, Double> entry : values.entrySet()) {
            if (entry.getValue() >= weightMin) {
                print += voc.get(entry.getKey()) + " : " + twoDecimals.format(entry.getValue()) + "; ";
            }
        }

        return print;
    }
}
